import type { Directive } from 'vue'

// 动画持续时间，ms
const DURATION = 1500

/**
 * @description 基于Vue3自定义指令，实现数字递增动画效果
 *
 * @example     `<div v-increase="100"></div>`
 */
export const increase: Directive = {
  // 在绑定元素的父组件
  // 及他自己的所有子节点都挂载完成后调用
  mounted(el, binding) {
    const { value: maxCount, modifiers } = binding
    el.$animation = animate((progress) => {
      // 数字千分位加逗号,保留两位小数点
      el.innerText = (maxCount * progress || 0)
        .toFixed(modifiers.no ? 0 : 2)
        .replace(/\B(?=(\d{3})+(?!\d))/g, ',')
    }, DURATION)
  },
  // 监听值变化
  beforeUpdate(el, binding, current, prev) {
    const { value: maxCount, modifiers } = binding
    if (maxCount !== prev?.props?.value) {
      el.$animation.cancel()
      el.$animation = animate((progress) => {
        el.innerText = (maxCount * progress || 0)
          .toFixed(modifiers.no ? 0 : 2)
          .replace(/\B(?=(\d{3})+(?!\d))/g, ',')
      }, DURATION)
    }
  },
  // 绑定元素的父组件卸载后调用
  unmounted(el) {
    el.$animation.cancel()
  }
}

/**
 * @description             基于requestAnimationFrame，实现在持续时间内执行动画的方法
 *
 * @param fn                动画执行函数，参数progress表示完成的进度
 * @param duration          动画持续时间
 * @returns {object.cancel} 取消动画
 */
export const animate = function (fn: (progress: number) => void, duration: number) {
  const animate = () => {
    animation = requestAnimationFrame(animate)

    const now = new Date().getTime()
    const progress = Math.floor(((now - START) / duration) * 100) / 100
    fn(progress > 1 ? 1 : progress)
    // 到达持续时间，结束动画
    if (now - START > duration) {
      cancel()
    }
  }

  const cancel = () => {
    cancelAnimationFrame(animation)
  }

  const START = new Date().getTime()

  let animation = requestAnimationFrame(animate)
  return {
    cancel: () => {
      cancelAnimationFrame(animation)
    }
  }
}
